Repository navigation
fix(plugin-email)!: the email template send judges the served holders, so a disabled package holds no name at the send (ADR-0150 D3) - #33361
objectstack-fleet[bot] wants to merge 5 commits into
Conversation
When a holder of a template name is disabled, the send resolves the name over the served holders, so a disabled package is never the answer and a refusal names only the packages that serve the name. The answer is only ever an address whose list holds no member of a disabled holder's side: the resolved served holder's own address, the environment-wide list for the environment's own item, or no template when the caller's own holding is the disabled one or no served holder answers it. With no holder disabled, every read is the one it was. Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
…r a real stored pair row Replaces the parity pin (a disabled holder still counted at the send) with the declared answers: two holders with each set disabled, a dependent's addition enabled and disabled, one holder beside the environment's item (registered first or last) or a package-less stored row, and an auth name one package overrides, every caller class, en-US and fr-FR, both reads and both registration orders, over the protocol's execution list with a real stored customization-pair row. CONTROLs: nothing disabled; one holder. Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
… the closure re-resolve The first check repeated what the re-resolve answers: a send resolved to the built-ins is resolved again over the served holders and read at the built-ins' own address. Ablating it moved no answer of the pins or of the probe matrix. Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
…36-38) Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
…ocol to source, and answer the ADR-0087 question The engine double the send pins read stored rows through opens findOne with assertEngineFindOnePredicate, and the pinned ledger records it. The protocol the pins read through resolves to this checkout's source. The changeset says why no ADR-0087 entry is owed: every moved answer follows a package's enabled state. Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check6 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 7 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b15c702f304777010f7f6ac8734277a513317c63 && git checkout b15c702f304777010f7f6ac8734277a513317c63
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d3f3f4abf9f392a49fecf77f2175424fe1680bef 4419624c1a5eab1741ba6f47502180d001bd6b93 && git checkout -B drift-repro d3f3f4abf9f392a49fecf77f2175424fe1680bef && git merge --no-ff 4419624c1a5eab1741ba6f47502180d001bd6b93
node scripts/docs-audit/affected-docs.mjs --json d3f3f4abf9f392a49fecf77f2175424fe1680bef |
|
domain:server#5 · os-elon-musk · session_016WV4cyrFFk86Jqf7Mp5Rzw |
|
Contract-review-claim: session_016WV4d1PGptBW3QRQNmy5e6 · head 4419624 · 2026-11-14T18:12:36Z review#3. |
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentRow 37, as asked: accepted as declared (no template). Under ADR-0150 D4, an environment item or package-less row for a name a package holds is that package's customization. The registry already answers it that way ( The rest holds: judging the send over the served holders completes D3 at the last door #32183 left at main (R4-A). Choosing the address over filtering is forced by what the execution list carries, and it keeps the member rule. Implemented-by: VERDICT: PASS |
Requested by Elon · project thread
Part of #32670
Clause-②: yes (narrowing) — widened: a send of a template name an enabled and a disabled package hold, made with no package or from a caller whose closure holds both, is served the enabled holder's template where it was refused naming both. Narrowed: a send that only a disabled holder's side answered (its customization pair's row, a dependent's added locale, the environment's item or a package-less stored row beside a name only it holds) finds no template; a send of an auth name whose one overriding package is disabled is served the built-in copy. Refusals name only the packages that serve the name. With no holder of the name disabled, every answer is unchanged.
Metadata-protocol: none
Skills-docs: none — no skill under
skills/**and no page undercontent/docs/**teaches what an email template send answers when a package holding the name is disabled.维护者速读
template-loader.ts的bundleAddressOf)在模板名有持有包被停用时,只按"仍在服务的持有包"解析名字,并且只从不含停用包成员的地址读取模板(解析出的持有包自己的地址,或内置模板的地址);停用包自己发送、或没有任何在服务持有包能回答的发送,一律"找不到模板"。没有持有包被停用时,行为与 main 完全相同。template-loader.ts一个文件。_env单元格与 R4-G 流程门不在本 PR(见 Acceptance notes)。packages/plugins/plugin-email/src/template-loader.ts(servedBundleAddressOf,尤其"读哪个地址"的分支);packages/plugins/plugin-email/src/template-loader.test.ts(新 describe 的期望表);.changeset/32670-email-send-served-holders.md(逐行 FROM → TO)。pnpm --filter @objectstack/plugin-email exec vitest run src/template-loader.test.ts,预期 76 个测试全部通过。_env单元格(需要改packages/metadata-protocol,属 E1 区域)与 R4-G 流程门的去向;本 PR 落地须等 metadata-protocol: a name held only by stored rows of two packages, one disabled, answers by save order, and /layers takes the first composite when the environment holds its own item (ADR-0150 D3) #32354。What changes
createRegistryEmailTemplateLoader'sbundleAddressOf(packages/plugins/plugin-email/src/template-loader.ts):main's, unchanged (resolve, count and the shared view over every holder, which are the served holders here). Measured: 0 moved answers with nothing disabled, across the probe matrix below.servedBundleAddressOf): the name is resolved over the served holders, so a disabled package is never the answer and a refusal names only the packages that serve the name. The bundle is read only where no member of a disabled holder's side is listed:mainalready read a name two packages hold);NO_BUNDLE, no list is read);_packageIdthere is the slot it fills, not the package that stores it. A pair row filling a locale a dependent also adds wears the dependent's id; the environment's item over a disabled holder's slot wears the disabled holder's id. So the member rule of contract review 6819947096 (a send returns only members stored under the resolved holder, its pair, a dependent whose closure holds it, or no package) is met by choosing the address, never by filtering_packageId.The card's rows 27 to 31 (PR #32497's round-3 table, withdrawn under R4-A) are restored, and rows 36 to 38 are declared. Numbering: 27 to 31 follow the round-3 changeset's order (rows 29 and 30 are the two refusals naming the enabled holder, per the red-line read 6819956008); 32 to 35 stay #32497's.
main)null)outside-closurenaming the enabled holderoutside-closurenaming bothoutside-closurenaming the enabled holderoutside-closurenaming it for a caller with no pathauth.*) whose one overriding package is disabledFirst-party count 0 on every row: no first-party composition ships an email template name two packages hold, or disables a package that holds one.
Tests
packages/plugins/plugin-email/src/template-loader.test.ts: the R4-A parity describe (a disabled holder still counts at the send), whose premise this card reverses, is replaced bya disabled package holds no name at the send (ADR-0150 D3, #32670):SchemaRegistryand the protocol's realgetMetaItemsForExecution(ObjectStackProtocolImplementation, aliased to this checkout's source) over storedsys_metadatarows: ALPHA's customization pairALPHA.customstores an fr-FR member row; ADDER depends on ALPHA only and adds fr-FR. No double overregistry.listItems. The engine double opensfindOnewithassertEngineFindOnePredicate, and carries no write verb.null, ALPHA, BETA, a closure holding both, a closure through ALPHA only, neither, the dependent, the built-in package.codeRESOURCE_CONFLICT,status409) and its candidates.Acceptance notes
_envcell of objectql + metadata-protocol + runtime: by-name doors judge a name's holders over the served list, so a disabled package neither refuses nor is named (ADR-0150 D3; #32101 F4 class) #32183's row 17 (ruling R4-B2). Moving_envof a name only disabled packages hold frommain's 409 to row 17's 404 needspackages/metadata-protocol/src/**, which this lane may not edit (area E1):admitEnvironmentQualifier(protocol-exact-qualifier.ts) admits the read when no package serves the name, and the_envmark has to reach the item face (protocol-meta-item-face.ts,disabledOnlyHolderOf) through both transports (packages/runtime/src/domains/meta.tsat itsadmitEnvironmentQualifiercall, andpackages/rest/src/meta-qualified-item-route.ts), so the face answers the withheld 404 and never the disabled holder's pair row. Raised for decision in the report; the card stays open (Part of).mainand carries their served judgment to this card (flowServiceForCaller,endpointPackageOf,flowWalkReads,flowHolderForCallerin runtimeflow-slot-view.ts;bareNameRefusalandadmitAutomationAddress's shared branch in runtimeautomation-address.ts;heldByMany,sharedFlowNameRefusalandresolveFlowKey's shared branch in service-automationengine.ts; and the dispatch keyflowDispatchHolder). A runtime disable keeps a disabled package's engine slot armed, so a served read there moves those doors both ways; that is a design question, raised for decision in the report.getEnvironmentScopedItemis undefined), and the execution list stamps the item with the disabled holder's slot. Sending nothing is the conservative answer (red line: resolution is never loosened); the reviewer may rule otherwise.servedBundleAddressOf(a resolve to the environment's own item, read environment-wide as before) is unreachable in the probe matrix: replaced by no template, 0 of 31,104 answers move. It is kept asmain's answer for that resolve.mainanswers some one-holder sends beside the environment's item in an order-dependent way (the en-US slot answers the holder's template in one registration order and the environment's item in the other), the same on both trees with nothing disabled. Not moved here; the pins use no such CONTROL.auth.*names) answers like any other package holder; measured in the matrix, not pinned.Verification
All at
4419624c1aunless named.Probe matrix (FROM = `main`'s loader, TO = this head's, one process, same registry per answer)
31,104 answers: one or two holders with every subset disabled; beside nothing, the environment's item registered first or last, or a package-less stored row; with and without the pair's stored fr-FR row; no dependent, the dependent enabled or disabled; no built-in, the built-in holding a non-auth name, or an auth name; 9 callers; en-US and fr-FR; both reads; both orders.
main1,127; this head 107, all 107 unmoved frommainand all in nothing-disabled states (the built-in package as caller of a one-holder non-auth name, R4-F's shape); new ones: 0.Ablations (each through `scripts/ablation-replace.mjs`, restored to the HEAD blob)
bundleAddressOfnever callsservedBundleAddressOf): 32 of 76 red, every disabled case in both orders and both reads; the 12 CONTROL tests and the other 32 stay green.26768975c). A2, A4 and A5 ran atb82d77303, whosetemplate-loader.tsdiffers from the head's only by that deleted line; A1 ran again at the head.Tests and gates
pnpm --filter @objectstack/plugin-email test: Test Files 53 passed (53), Tests 1010 passed (1010) (lock VERDICT command-exit 0).pnpm --filter @objectstack/plugin-email typecheck(tsc, thencheck:test-typecheck): exit 0,check:test-typecheck: OK.eslint --no-inline-configon the two touched source files: exit 0.dispatch-gates.mjs --commands, 79 commands): 75 exit 0 on the first run;check-adr-0087-registration,check:engine-double-contractandcheck:test-source-aliaswere red on the first run and are exit 0 after4419624c1a(the ADR-0087 disposition, the pinnedfindOneand its ledger line, the protocol's source alias).check:dual-build-cjs-loads: NOT MEASURED, reason: PREREQUISITE NOT MET (it reads every package'sdist/; CI runs it).@objectstack/plugin-email(60 downstream packages, throughrestandclient): not run locally; the public surface is byte-unchanged (no export, type or signature moves; the change is module-internal), and Test Core runs the affected set. The full dogfood battery is owed by the narrowing arm and is reported with the dev report.Generated by Claude Code